|
1
|
|
|
var assert = require('assert'); |
|
2
|
|
|
var sjcl = require('sjcl'); |
|
3
|
|
|
var KeyDerivation = require('./keyderivation'); |
|
4
|
|
|
var randomBytes = require('randombytes'); |
|
5
|
|
|
|
|
6
|
|
|
var Encryption = { |
|
7
|
|
|
defaultSaltLen: 10, /* can permit changes, no more than 512 bit (128 bytes) */ |
|
8
|
|
|
tagLenBits: 128, /* can permit changes */ |
|
9
|
|
|
ivLenBits: 128, /* fixed */ |
|
10
|
|
|
ivLenWords: 128 / 32 |
|
11
|
|
|
}; |
|
12
|
|
|
|
|
13
|
|
|
Encryption.generateSalt = function() { |
|
14
|
|
|
return randomBytes(this.defaultSaltLen); |
|
15
|
|
|
}; |
|
16
|
|
|
|
|
17
|
|
|
Encryption.generateIV = function() { |
|
18
|
|
|
return randomBytes(this.ivLenBits / 8); |
|
19
|
|
|
}; |
|
20
|
|
|
|
|
21
|
|
|
Encryption.encrypt = function(pt, pw, iterations) { |
|
22
|
|
|
var salt = this.generateSalt(); |
|
23
|
|
|
var iv = this.generateIV(); |
|
24
|
|
|
|
|
25
|
|
|
iterations = typeof iterations === 'undefined' ? KeyDerivation.defaultIterations : iterations; |
|
26
|
|
|
return this.encryptWithSaltAndIV(pt, pw, salt, iv, iterations); |
|
27
|
|
|
}; |
|
28
|
|
|
|
|
29
|
|
|
Encryption.encryptWithSaltAndIV = function(pt, pw, saltBuf, iv, iterations) { |
|
30
|
|
|
assert(pt instanceof Buffer, 'pt must be provided as a buffer'); |
|
|
|
|
|
|
31
|
|
|
assert(pw instanceof Buffer, 'pw must be provided as a buffer'); |
|
32
|
|
|
assert(iv instanceof Buffer, 'IV must be provided as a buffer'); |
|
33
|
|
|
assert(saltBuf instanceof Buffer, 'saltBuff must be provided as a buffer'); |
|
34
|
|
|
assert(iv.length === 16, 'IV must be exactly 16 bytes'); |
|
35
|
|
|
|
|
36
|
|
|
var SL = (new Buffer(1)); |
|
37
|
|
|
var S = saltBuf; |
|
38
|
|
|
var I = new Buffer(4); |
|
39
|
|
|
SL.writeUInt8(saltBuf.length); |
|
40
|
|
|
I.writeUInt32LE(iterations); |
|
41
|
|
|
var header = SL.toString('hex') + S.toString('hex') + I.toString('hex'); |
|
42
|
|
|
|
|
43
|
|
|
var key = sjcl.codec.hex.toBits(KeyDerivation.compute(pw, saltBuf, iterations).toString('hex')); |
|
44
|
|
|
var ct_t = sjcl.mode.gcm.encrypt( |
|
45
|
|
|
new sjcl.cipher.aes(key), |
|
46
|
|
|
sjcl.codec.hex.toBits(pt.toString('hex')), |
|
47
|
|
|
sjcl.codec.hex.toBits(iv.toString('hex')), |
|
48
|
|
|
sjcl.codec.hex.toBits(header), |
|
49
|
|
|
this.tagLenBits |
|
50
|
|
|
); |
|
51
|
|
|
|
|
52
|
|
|
// iter || saltLen8 || salt || iv || tag || ct |
|
53
|
|
|
return new Buffer([header, iv.toString('hex'), sjcl.codec.hex.fromBits(ct_t)].join(''), 'hex'); |
|
54
|
|
|
}; |
|
55
|
|
|
|
|
56
|
|
|
Encryption.decrypt = function(ct, pw) { |
|
57
|
|
|
assert(ct instanceof Buffer, 'cipherText must be provided as a Buffer'); |
|
|
|
|
|
|
58
|
|
|
assert(pw instanceof Buffer, 'password must be provided as a Buffer'); |
|
59
|
|
|
var copy = new Buffer(ct, 'hex'); |
|
60
|
|
|
var c = 0; |
|
61
|
|
|
|
|
62
|
|
|
var saltLen = copy.readUInt8(c) ; c += 1; |
|
63
|
|
|
var salt = copy.slice(1, c + saltLen); c += saltLen; |
|
64
|
|
|
var iterations = copy.readUInt32LE(c); c += 4; |
|
65
|
|
|
var header = copy.slice(0, c); |
|
66
|
|
|
|
|
67
|
|
|
var iv = copy.slice(c, 16 + c); c += 16; |
|
68
|
|
|
var ct_t = copy.slice(c); |
|
69
|
|
|
|
|
70
|
|
|
// SaltBuf is required for KeyDerivation. Convert to sjcl where required. |
|
71
|
|
|
var key = KeyDerivation.compute(pw, salt, iterations); |
|
72
|
|
|
var plainText = sjcl.mode.gcm.decrypt( |
|
73
|
|
|
new sjcl.cipher.aes(sjcl.codec.hex.toBits(key.toString('hex'))), |
|
74
|
|
|
sjcl.codec.hex.toBits(ct_t.toString('hex')), |
|
75
|
|
|
sjcl.codec.hex.toBits(iv.toString('hex')), |
|
76
|
|
|
sjcl.codec.hex.toBits(header.toString('hex')), |
|
77
|
|
|
this.tagLenBits |
|
78
|
|
|
); |
|
79
|
|
|
return new Buffer(sjcl.codec.hex.fromBits(plainText), 'hex'); |
|
80
|
|
|
}; |
|
81
|
|
|
|
|
82
|
|
|
module.exports = Encryption; |
|
83
|
|
|
|
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.